//#define CXXOMFORT_NOTICES 2
#define CXXOFLAG_optional_specialize_fundamentals

#include <cxxomfort/cxxomfort.hpp>
#include <cxxomfort/optional.hpp> // this makes use of cxxo::optional below C++17
#include <cxxomfort/functional.hpp>
#include <cxxomfort/cstd/optional>
#include <string>
#include <iostream>
#include <iomanip>
#include <cxxomfort/library/functional.hpp> // function_ref
#include <print_traits.hpp>

typedef float A;
typedef std::pair<int,std::string> B;
typedef std::optional<float> OF;

// monadic transform


struct I {
    //int v1 = 4;
    //float v2 = 0.2;
#if (CXXOMFORT_CXX_STD >= 2011)
    I () = default;
    I (I const&) {}
    I (I&&) noexcept = default;
    virtual ~I () {}
#endif

};

static bool istoolong(std::string const& s) {
	return s.length() > 9;
}

template <typename T>
void show_optional (std::ostream& os, std::optional<T> const& o_) {
	using namespace std;
	os<< "optional< "<< cxxomfort::library::type_name<T>()<< " > "<< flush;
	os<< "[ state: "<< o_.has_value() << flush;
	os<< " value: ";
	if (o_.has_value()) { os<< *o_; }
	else { os<< "[None]"; }
	os<< " ]"<< flush;
}


int main () {
    using namespace std;
    cxxomfort::output_info(stdout); cout<< endl;

try {
	
    print_traits< optional<float> >(cout); 
    print_traits_construction< optional<float> >(cout);
    cout<< endl;
    print_traits< optional< string > >(cout); 
    print_traits_construction< optional< string> >(cout);
    cout<< endl;
    print_traits< optional<bool> >(cout); 
    print_traits_construction< optional<bool> >(cout);
    cout<< endl;
    print_traits< I >(cout); 
    print_traits_construction< I >(cout);
    cout<< endl;
    //print_traits< std::string >(cout);
    cout<< "optional<int> : t1: empty optional; t2: nullopt; t3: default-constructed; t4: initialized"<< endl;
    optional<int> t1;
    optional<int> t2(nullopt);
    optional<int> t3(std::in_place);
    optional<int> t4(2);
    cout<< "t1.has_value(): "<< t1.has_value()<< endl;
    cout<< "t2.has_value(): "<< t2.has_value()<< endl;
    cout<< "t3.has_value(): "<< t3.has_value()<< endl;
    cout<< "t4.has_value(): "<< t4.has_value()<< endl;
    t2= t4; cout<< "t2 = t4"<< endl;
    cout<< "t2.has_value(): "<< t2.has_value()<< endl;

    cout<< "\nTesting optional<float>: \n";
    OF of1, of2(-4.4), of3;
    cout<< "of1 : "; show_optional(cout, of1); cout<< endl;
    cout<< "of2 : "; show_optional(cout, of2); cout<< endl;
    cout<< "of3 : "; show_optional(cout, of3); cout<< endl;
    cout<< "\nTesting make_optional<...>: "<< endl;
    cout<< "int      "<< flush<< make_optional<int>().has_value()<< endl;
    cout<< "long     "<< flush<< make_optional<long>(-31).value_or(0)<< endl;
    cout<< "string   "<< make_optional<string>().value_or("[empty]")<< endl; 
    cout<< "string   "<< make_optional<string>(4u, '4').value_or("[empty]")<< endl;
    cout<< "*"<< endl;
    OF foo;
    cout<< "foo: "<< flush<< foo.has_value()<< endl;
    cout<< "foo: "<< flush;
    if ( explicit_cast<bool>(foo) ) {
        cout<< *foo<< endl;
    } else {
        cout<< "(nothing)"<< endl;
    }

	optional<string> os1, os2("A"), os3("3"), os4(os1);
	cout<< "os2.has_value(): "<< os2.has_value()<< endl;
	os2=os3;
	cout<< "os2.has_value(): "<< os2.has_value()<< endl;
	cout<< *os2<< endl;
	os2=os4;
	cout<< "os2.has_value(): "<< os2.has_value()<< endl;
	cout<< os2.value_or("?")<< endl;
    
    cout<< endl;
    cout<< "Testing optional::map_to, optional::map_or:"<< endl;
    optional<string> xs1, xs2("Hello World");
    optional<size_t> xz1= xs1.map_to<size_t>(cxxomfort::fix::mem_fn(&string::size));
    optional<size_t> xz2= xs2.map_to<size_t>(mem_fn(&string::size));
    cout<< xz1.value_or(0)<< endl;
    cout<< xz2.value_or(0)<< endl;

	#if (CXXOMFORT_CXX_STD>=2011)
	cout<< "Testing optional::map"<< endl;
    optional<size_t> xz3= xs1.map(mem_fn(&string::size) );
    auto xz4= xs2.map(mem_fn(&string::size) );
    //optional<int> xz5 = xz4;
    cout<< xz3.value_or(-1)<< endl;
    cout<< xz4.value_or(-1)<< endl;
	#endif

    cout<< endl;
    cout<< "Filter "<< *xs2<< endl;
    optional<string> xs3;
    xs3= xs2.filter(bind(istoolong, placeholders::_1));
    cout<< "Done"<< endl;
    cout<< xs3.has_value()<< endl;
    xs2= string("A");
    cout<< "xs2: "<< xs2.value_or("None")<< endl;
    cout<< "xs3: "<< xs3.value_or("None")<< endl;
    xs3= nullopt;
    cout<< "xs3: "<< xs3.value_or("None")<< endl;

} catch (std::exception& E) {
    cerr<< E.what()<< endl;
}
    
}


